# Copyright (c) 2008-2009 Mikhail Chernov 
#
# This file is part of dcExplorer.
#
# dcExplorer is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
#
# dcExplorer is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with dcExplorer.  If not, see <http://www.gnu.org/licenses/>.

package dcExplorer::wxui::frame::Main;
use strict;

use Settings;
use Error qw(:try);
use Accounts;
use wxui::frame::Fleet;
use wxui::frame::Planet;

use wxui::widget::Empire;
use wxui::dialog::Credentials;
use wxui::dialog::FleetNames;
use wxui::dialog::Text;

use core::Empire;

use Wx;
use base 'Wx::Frame';
use Wx::Event qw(EVT_MOTION EVT_KEY_DOWN);
use Wx::Event qw(EVT_MENU EVT_MOUSE_EVENTS);
use Wx qw(wxMOUSE_BTN_LEFT wxMOUSE_BTN_RIGHT);
use Wx qw(wxID_EXIT);
use Wx qw(wxYES_NO wxID_YES wxID_OK);

sub makeMenu
{
	my $self = shift;

	$self->{Menu}->{File} = Wx::Menu->new();
	
	EVT_MENU( $self, $self->{Menu}->{File}->Append(-1, "&Export to csv")->GetId(), sub {$_[0]->ExportToCSV()} );
	EVT_MENU( $self, $self->{Menu}->{File}->Append(-1, "&Import from csv")->GetId(), sub {$_[0]->ImportFromCSV()} );
	$self->{Menu}->{File}->AppendSeparator();
	EVT_MENU( $self, $self->{Menu}->{File}->Append(-1, "&Run script")->GetId(), sub {$_[0]->RunScript} );
	$self->{Offline} = $self->{Menu}->{File}->AppendCheckItem(-1, "&Offline\tCtrl+O");
	EVT_MENU( $self, $self->{Menu}->{File}->Append(-1, "Send commands")->GetId(), sub {$_[0]->SubmitActions} );
	$self->{Menu}->{File}->AppendSeparator();
	EVT_MENU( $self, $self->{Menu}->{File}->Append(wxID_EXIT, "E&xit\tCtrl+X")->GetId(), sub {$_[0]->Close} );

	$self->{Menu}->{Fleet} = Wx::Menu->new();
	EVT_MENU( $self, $self->{Menu}->{Fleet}->Append(-1, "&View\tCtrl+F")->GetId(), sub {$_[0]->FleetDetails} );
	$self->{Menu}->{Fleet}->AppendSeparator();
	EVT_MENU( $self, $self->{Menu}->{Fleet}->Append(-1, "&Hide\tCtrl+S")->GetId(), sub {$_[0]->FleetHide} );
	EVT_MENU( $self, $self->{Menu}->{Fleet}->Append(-1, "&Rename\tCtrl+R")->GetId(), sub {$_[0]->RenameFleet} );
	EVT_MENU( $self, $self->{Menu}->{Fleet}->Append(-1, "&Batch Rename\tCtrl+E")->GetId(), sub {$_[0]->BatchRenameFleet} );
	EVT_MENU( $self, $self->{Menu}->{Fleet}->Append(-1, "&Unload\tCtrl+U")->GetId(), sub {$_[0]->UnloadFleet} );
	EVT_MENU( $self, $self->{Menu}->{Fleet}->Append(-1, "&Dismiss\tCtrl+D")->GetId(), sub {$_[0]->DismissFleet} );
	$self->{Menu}->{Fleet}->AppendSeparator();
	EVT_MENU( $self, $self->{Menu}->{Fleet}->Append(-1, "&Disband")->GetId(), sub {$_[0]->DisbandFleet} );

	$self->{Menu}->{Planet} = Wx::Menu->new();
	EVT_MENU( $self, $self->{Menu}->{Planet}->Append(-1, "&Harrison\tCtrl+H")->GetId(), sub {$_[0]->HarrisonDetails} );
	EVT_MENU( $self, $self->{Menu}->{Planet}->Append(-1, "&Queue\tCtrl+Q")->GetId(), sub {$_[0]->QueueDetails} );
	$self->{Menu}->{Planet}->AppendSeparator();
	EVT_MENU( $self, $self->{Menu}->{Planet}->Append(-1, "&Rename\tCtrl+M")->GetId(), sub {$_[0]->RenamePlanet} );

	$self->{MenuBar} = Wx::MenuBar->new();
	$self->{MenuBar}->Append($self->{Menu}->{File}, "&File");
	$self->{MenuBar}->Append($self->{Menu}->{Fleet}, "F&leet");
	$self->{MenuBar}->Append($self->{Menu}->{Planet}, "&Planet");
	$self->SetMenuBar($self->{MenuBar});
	$self->makeMenuAccount();
	$self->makeMenuTurn();
}

sub makeMenuAccount
{
	my $self = shift;

	$self->{Menu}->{Account} = Wx::Menu->new();	

	my $hotkey = 0;
	for my $name ($self->{Accounts}->names())
	{
		EVT_MENU( $self, $self->{Menu}->{Account}->Append(-1, $name."\tAlt+".(++$hotkey))->GetId(), sub {$_[0]->SelectAccount($name)} );
	}
	$self->{Menu}->{Account}->AppendSeparator();
	EVT_MENU( $self, $self->{Menu}->{Account}->Append(-1, "&New\tCtrl+N")->GetId(), sub {$_[0]->AddAccount} );

	$self->{MenuBar}->Append($self->{Menu}->{Account}, "&Account");
}

sub makeMenuTurn
{
	my $self = shift;
	$self->{Menu}->{Turn} = Wx::Menu->new();
	if (defined($self->{Account})) 
	{
		EVT_MENU( $self, $self->{Menu}->{Turn}->Append(-1, "&Download\tF5")->GetId(), sub {$_[0]->Download} );
		EVT_MENU( $self, $self->{Menu}->{Turn}->Append(-1, "&Next\tCtrl+PgUp")->GetId(), sub {$_[0]->SelectNextTurn()} );
		EVT_MENU( $self, $self->{Menu}->{Turn}->Append(-1, "&Prev\tCtrl+PgDn")->GetId(), sub {$_[0]->SelectPrevTurn()} );
		$self->{Menu}->{Turn}->AppendSeparator();
		my $index = 0;
		for my $turn (reverse @{$self->{Account}->getTurnList()})
		{
			EVT_MENU( $self, $self->{Menu}->{Turn}->Append(-1, $turn.($index == 0 ? " (Last)" : ""))->GetId(), sub {$_[0]->SelectTurn($turn)} );
			last if ++$index == 10;
		}
	}
	$self->{MenuBar}->Append($self->{Menu}->{Turn}, "&Turn");
}

sub new
{
	my $proto = shift;
	my $class = ref($proto) || $proto;
	my $self = $class->SUPER::new(undef, -1, "dcExplorer", [-1, -1], $Settings::DefaultSize); 

	$self->{Accounts} = shift;
	$self->{AccountName} = ($self->{Accounts}->names())[0];
	$self->{Account} = $self->{Accounts}->account($self->{AccountName});

	$self->{EmpireWidget} = dcExplorer::wxui::widget::Empire->new($self, -1, $self->{Account}->getEmpire());
	$self->{EmpireWidget}->moveTo($self->{Account}->{'x'}, $self->{Account}->{'y'});

	EVT_MOUSE_EVENTS($self, sub {$_[0]->OnMouseEvent($_[1])});
	$self->makeMenu;
	return $self;
}

# Actions handling routines

sub RunScript
{
	my $self = shift;
	my $textDialog = dcExplorer::wxui::dialog::Text->new($self, "", "Enter script text");
	if ($textDialog->ShowModal() == wxID_OK)
	{
		my $text = $textDialog->GetValue();
		my $Empire = $self->{Account}->getEmpire();
		eval $text;
		if ($@)
		{
			$self->{Account}->getEmpire()->commandQueue()->clear();
			Wx::MessageDialog->new($self, "$@", "Error!")->ShowModal();
		}
		else
		{
			$self->SubmitActions;
			$self->{EmpireWidget}->refreshAll;
		}
	}
}

sub RenamePlanet
{
	my $self = shift;
	my $planet = $self->{EmpireWidget}->getSelectedPlanet();
	if (defined $planet && $planet->getProp('my'))
	{
		my $nameDialog = Wx::TextEntryDialog->new($self, "New planet name:", "Rename planet", $planet->getProp('name'));
		if ($nameDialog->ShowModal() == wxID_OK)
		{
			$planet->changeName($nameDialog->GetValue());
			$self->SubmitActions;
			$self->{EmpireWidget}->refreshPlanet;
		}
	}
}

sub HarrisonDetails
{
	my $self = shift;
	my $harrison = $self->{EmpireWidget}->getSelectedHarrison();
	if (defined $harrison)
	{
		dcExplorer::wxui::frame::Fleet->new($self, -1, $harrison)->Show;
	}
}

sub QueueDetails
{
	my $self = shift;
	my $queue = $self->{EmpireWidget}->getSelectedQueue();
	if (defined $queue)
	{
		dcExplorer::wxui::frame::Planet->new($self, -1, $queue)->Show;
	}
}

sub FleetDetails
{
	my $self = shift;
	dcExplorer::wxui::frame::Fleet->new($self, -1, $_)->Show foreach ($self->{EmpireWidget}->getSelectedFleets());
}

sub RenameFleet
{
	my $self = shift;
	my @fleets = $self->{EmpireWidget}->getSelectedControlledFleets();
	return unless @fleets;
	for my $fleet (@fleets)
	{
		my $fleetNames = dcExplorer::wxui::dialog::FleetNames->new($self, $fleet->getProp('name'), $fleet->getProp('ext_name'));
		if ($fleetNames->ShowModal() == wxID_OK)
		{
			$fleet->Rename($fleetNames->GetName(), $fleetNames->GetExtName());
		}
	}
	$self->SubmitActions;
	$self->{EmpireWidget}->refreshFleets;
}

sub BatchRenameFleet
{
	my $self = shift;
	my @fleets = $self->{EmpireWidget}->getSelectedControlledFleets();
	return unless @fleets;
	my $fleet = $fleets[0];
	my $fleetNames = dcExplorer::wxui::dialog::FleetNames->new($self, $fleet->getProp('name'), $fleet->getProp('ext_name'));
	if ($fleetNames->ShowModal() == wxID_OK)
	{
		for my $fleet (@fleets)
		{
			$fleet->Rename($fleetNames->GetName(), $fleetNames->GetExtName());
		}
	}
	$self->SubmitActions;
	$self->{EmpireWidget}->refreshFleets;
}

sub UnloadFleet
{
	my $self = shift;
	my @fleets = $self->{EmpireWidget}->getSelectedControlledFleets();
	return unless @fleets;
	for my $fleet (@fleets)
	{
		for my $unit (grep {$_->getProp('is_transportable')} $fleet->getAllUnits)
		{
			$unit->transferToAnotherFleet(0);
		}
	}
	$self->SubmitActions;
	$self->{EmpireWidget}->refreshFleets;
}

sub DismissFleet
{
	my $self = shift;
	my @fleets = $self->{EmpireWidget}->getSelectedControlledFleets();
	return unless @fleets;
	for my $fleet (@fleets)
	{
		for my $unit (grep {$_->getProp('is_transportable')} $fleet->getAllUnits)
		{
			$unit->transferToAnotherFleet(0);
		}
		for my $unit (grep {!$_->getProp('is_transportable')} $fleet->getAllUnits)
		{
			$unit->transferToAnotherFleet(0);
		}
	}
	$self->SubmitActions;
	$self->{EmpireWidget}->refreshFleets;
}

sub DisbandFleet
{
	my $self = shift;
	my @fleets = $self->{EmpireWidget}->getSelectedControlledFleets();
	return unless @fleets;
	return unless Wx::MessageDialog->new($self, 
			"Disband the following fleets:\n".join("\n", map {$_->getProp('name')} @fleets)."?", 
			"Disband fleet(s)?", wxYES_NO)->ShowModal() == wxID_YES;
	for my $fleet (@fleets)
	{
		for my $unit (grep {$_->getProp('is_transportable')} $fleet->getAllUnits)
		{
			$unit->disband;
		}
		for my $unit (grep {!$_->getProp('is_transportable')} $fleet->getAllUnits)
		{
			$unit->disband;
		}
	}
	$self->SubmitActions;
	$self->{EmpireWidget}->refreshFleets;
}

sub FleetHide
{
	my $self = shift;
	my @fleets = $self->{EmpireWidget}->getSelectedControlledFleets();
	return unless @fleets;
	for my $fleet (@fleets)
	{
		$fleet->show($fleet->getProp('hidden') ^ defined($fleet->getProp('fleet_changes')));
	}
	$self->SubmitActions;
	$self->{EmpireWidget}->refreshFleets;
}

sub FleetSend
{
	my $self = shift;
	my $x = shift;
	my $y = shift;	

	my @list = $self->{EmpireWidget}->getSelectedControlledFleets();
	my @outOfRange;
	my @inTransit;
	my @atDestination;
	my @send;
	for my $fleet (@list)
	{
		push(@inTransit, $fleet), next if ($fleet->getProp('in_transit') && $fleet->getProp('turns_till_arrival'));
		if ($fleet->getProp('turns_till_arrival') > 0)
		{
			next unless !$Settings::CancelJumpQuestion || Wx::MessageDialog->new($self,
				'Fleet "'.$fleet->getProp('name').'" has its order already. Cancel?',
				'Cancel jump?', wxYES_NO)->ShowModal() == wxID_YES;
			$fleet->cancelJump();
		}
		my $dx = $x - $fleet->getProp('x');
		my $dy = $y - $fleet->getProp('y');
		my $d = sqrt($dx*$dx + $dy*$dy);
		push(@atDestination, $fleet), next if ($d == 0);
		push(@outOfRange, $fleet), next if ($fleet->getProp('fly_range') < $d);
		my $t = $d / $fleet->getProp('fly_speed');
		$t = int($t) + 1 if ($t > int($t));
		push(@send, [$fleet, $t]);
	}
	Wx::MessageDialog->new($self, 
		"The following fleets are in space and can't receive commands:\n".join("\n", map {$_->getProp('name')} @inTransit),
		"Can't send fleets!")->ShowModal() if scalar @inTransit;
	Wx::MessageDialog->new($self, 
		"The following fleets are at destination or already have an order to jump to it:\n".join("\n", map {$_->getProp('name')} @atDestination),
		"Can't send fleets!")->ShowModal() if scalar @atDestination && $Settings::AtDestinationWarning;
	Wx::MessageDialog->new($self,
		"The following fleets have the destination out of their ranges:\n".join("\n", map {$_->getProp('name')} @outOfRange),
		"Can't send fleets!")->ShowModal() if scalar @outOfRange;
	if (scalar @send)
	{
		if (!$Settings::JumpQuestion || Wx::MessageDialog->new($self, 
			"Send the following fleets:\n".join("\n", map {$_->[0]->getProp('name').' tta: '.$_->[1]} @send)."\n"."to the planet ".Tools::formatCoordinate($x).":".Tools::formatCoordinate($y)."?", 
			"Send fleet(s)?", wxYES_NO)->ShowModal() == wxID_YES)
		{
			for my $fleet (map {$_->[0]} @send)
			{
				$fleet->jump($x, $y);
			}
		}
	}
	$self->SubmitActions;
	$self->{EmpireWidget}->refreshAll;
}

# Account management

sub switchAccount
{
	my $self = shift;
	my $id = shift;
	$self->{AccountName} = $id;
	$self->{Account} = $self->{Accounts}->account($self->{AccountName});
	$self->{EmpireWidget}->moveTo($self->{Account}->{'x'}, $self->{Account}->{'y'});
}

sub AddAccount
{
	my $self = shift;
	my $credentials = dcExplorer::wxui::dialog::Credentials->new($self);
	if ($credentials->ShowModal() == wxID_OK)
	{
		try
		{
			my $name = $self->{Accounts}->Add($credentials->GetUsername(), $credentials->GetPassword());
			$self->{MenuBar}->Remove(4);
			$self->{MenuBar}->Remove(3);
			$self->switchAccount($name);
			$self->makeMenuAccount();
			$self->makeMenuTurn();
			$self->Download();
			Wx::MessageDialog->new($self, "Account \"".$name."\" was successfully added to the database.",
				"Account added")->ShowModal;
		}
		catch Error::Simple with
		{
			my $error = shift;
			Wx::MessageDialog->new($self, "$error", "Unable to log in!")->ShowModal();
		};
	}
}

sub SelectAccount
{
	my $self = shift;
	my $id = shift;
	return if defined($self->{AccountName}) && $id eq $self->{AccountName};
	
	$self->switchAccount($id);

	$self->{MenuBar}->Remove(4);
	$self->makeMenuTurn();
	$self->{EmpireWidget}->resetSelection;
	$self->{EmpireWidget}->setEmpire($self->{Account}->getEmpire());
}

sub Download
{
	my $self = shift;
	try 
	{
		my $turn = $self->{Account}->getEmpire()->getProp('turn') || 0;
		if ($turn != $self->{Account}->download(["all", "known_planets"])->getProp('turn'))
		{
			$self->{MenuBar}->Remove(4);
			$self->makeMenuTurn();
		}
		$self->{EmpireWidget}->setEmpire($self->{Account}->getEmpire());
	}
	catch Error::Simple with
	{
		my $error = shift;
		Wx::MessageDialog->new($self, "$error", "Unable to download!")->ShowModal();
	};
}

sub SelectTurn
{
	my $self = shift;
	my $id = shift;
	return if $id eq $self->{Account}->getEmpire->getProp('turn');
	my $account = $self->{Accounts}->account($self->{AccountName});
	$account->getTurn($id);
	$self->{EmpireWidget}->setEmpire($self->{Account}->getEmpire());
}

sub SelectNextTurn
{
	my $self = shift;
	my $turn = $self->{Account}->getEmpire()->getProp('turn');
	my ($next, @turns) = @{$self->{Accounts}->account($self->{AccountName})->getTurnList()};
	($next, @turns) = @turns while $next != $turn;
	($next, @turns) = @turns;
	$self->SelectTurn($next) if $next;
}

sub SelectPrevTurn
{
	my $self = shift;
	my $turn = $self->{Account}->getEmpire()->getProp('turn');
	my $prev = undef;
	my ($next, @turns) = @{$self->{Accounts}->account($self->{AccountName})->getTurnList()};
	$prev = $next, ($next, @turns) = @turns while $next != $turn;	
	$self->SelectTurn($prev) if $prev;
}

sub OnMouseEvent
{
	my $self = shift;
	my $event = shift;
	if ($event->ButtonDown(wxMOUSE_BTN_LEFT))
	{
		my ($x, $y) = $self->{EmpireWidget}->{MapWidget}->PixelToPlanet($event->GetX, $event->GetY);
		if ($event->CmdDown())
		{
			$self->FleetSend($x, $y);
		}
		else
		{
			$self->{EmpireWidget}->switchSelection($x, $y);
		}
	}
	unless ($self->{EmpireWidget}->getSelection())
	{
		$self->{EmpireWidget}->displayAt([$self->{EmpireWidget}->{MapWidget}->PixelToPlanet($event->GetX, $event->GetY)]);
	}
}

sub SubmitActions
{
	my $self = shift;
	return unless $self->{Account}->havePendingActions;
	unless ($self->{Offline}->IsChecked())
	{
		unless ($self->{Account}->submitActions)
		{
			Wx::MessageDialog->new($self, "It's suggested to reload the data to view current situation.", "Actions failed!")->ShowModal();
		}
	}
}

# Import & Export routines

my $filter = "CSV files (*.csv)|*.csv|All files (*.*)|*.*";
my $format = "x,y,o,e,m,t,s,owner_id,owner_name,name";
use Wx qw(wxFD_SAVE wxFD_OVERWRITE_PROMPT wxFD_OPEN wxFD_FILE_MUST_EXIST);

sub ExportToCSV
{
	my $self = shift;
	my $fileDialog = Wx::FileDialog->new($self, "Choose a file", "", "", $filter, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
	return unless ($fileDialog->ShowModal() == wxID_OK);
	open FILE, ">:utf8", $fileDialog->GetFilename();
	print FILE $format."\n";
	my $empire = $self->{Account}->getEmpire();
	for my $planet ($empire->planets->getAllKnown())
	{
		my $name = $planet->getProp('name');
		$name =~ s/"/""/g;
		my $owner_name = defined($planet->getProp('owner_id')) ? 
			$empire->players->get($planet->getProp('owner_id'))->getProp('name') : "";
		$owner_name =~ s/"/""/g;
   	printf FILE ("%d,%d,%d,%d,%d,%d,%d,%d,\"%s\",\"%s\"\n", (map {$planet->getProp($_) || 0} qw(x y o e m t s owner_id)), $owner_name, $name); 
	};
	close FILE;
}

sub ImportFromCSV
{
	my $self = shift;
	my $fileDialog = Wx::FileDialog->new($self, "Choose a file", "", "", $filter, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
	return unless ($fileDialog->ShowModal() == wxID_OK);
	open FILE, "<:utf8", $fileDialog->GetFilename();
	my $line = <FILE>;
	if ($line =~ /^$format$/)
	{
		my $empire = $self->{Account}->getEmpire();
		while ($line = <FILE>)
		{
			my ($x, $y, $o, $e, $m, $t, $s, $owner_id, $owner_name, $name) = 
				($line =~ /^(\d{1,4}),(\d{1,4}),(\d{1,2}),(\d{1,2}),(\d{1,2}),(\d{1,2}),(\d{1,2}),(\d+),"(.*)","(.*)"$/);
			next unless $x && $y;
			my $planet = $empire->planets->get($x, $y);
			if ($s)
			{
				$planet->setProp('o', $o);
				$planet->setProp('e', $e);
				$planet->setProp('m', $m);
				$planet->setProp('t', $t);
				$planet->setProp('s', $s);
			}
			if ($owner_id)
			{
				$owner_name =~ s/""/"/g;
				$name =~ s/""/"/g;
				$planet->setProp('owner_id', $owner_id);
				$planet->setProp('owner_name', $owner_name);
				my $player = $empire->players->get($owner_id);
				if (!$player->getProp('name'))
				{
					$player->setProp('name', $owner_name);
					$empire->players->set($player);
				}
			}
			$planet->setProp('name', $name);
			$empire->planets->set($planet);
		}
	}
	else
	{
		Wx::MessageDialog->new($self, "File format is not recognized, sorry!", "Wrong file format")->ShowModal();
	}
}

1;
